home *** CD-ROM | disk | FTP | other *** search
/ IRIX 6.5 Applications 2002 November / SGI IRIX 6.5 Applications 2002 November.iso / dev / insight_dev.idb / usr / share / Insight / bin / gifcomment.z / gifcomment
Text File  |  2002-10-15  |  9KB  |  344 lines

  1. #!/usr/bin/perl5
  2.  
  3. # Copyright 2002, Silicon Graphics, Inc.
  4. # All Rights Reserved.
  5. #
  6. # This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  7. # the contents of this file may not be disclosed to third parties, copied or
  8. # duplicated in any form, in whole or in part, without the prior written
  9. # permission of Silicon Graphics, Inc.
  10. #
  11. # RESTRICTED RIGHTS LEGEND:
  12. # Use, duplication or disclosure by the Government is subject to restrictions
  13. # as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  14. # and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  15. # successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  16. # rights reserved under the Copyright Laws of the United States.
  17.  
  18. ##########################################################################
  19. #
  20. # Read and set comments in a GIF file
  21. #
  22. # http://www.dcs.ed.ac.uk/home/mxr/gfx/2d/GIF89a.txt
  23. #
  24. # Version 1.0 - 12/3/01
  25. #
  26. ##########################################################################
  27.  
  28. # Global array to store existing comment information
  29. @g_comments = ();
  30.  
  31. undef $new_comment;
  32. $gif_file = '';
  33.  
  34. while($arg = shift(@ARGV)) {
  35.     if($arg eq '-c') {
  36.         $new_comment = shift(@ARGV);
  37.     } elsif($arg eq '-h') {
  38.         ShowUsage();
  39.         exit;
  40.     } elsif($arg =~ m#^-#) {
  41.         print STDERR "Unknown argument '$arg'\n\n";
  42.         ShowUsage();
  43.         exit(1);
  44.     } else {
  45.         $gif_file = $arg;
  46.         last;
  47.     }
  48.  
  49. if($gif_file eq '') {
  50.     ShowUsage();
  51.     exit(1);
  52. } elsif(! -e $gif_file) {
  53.     print STDERR "GIF file '$gif_file' doesn't exist.\n\n";
  54.     ShowUsage();
  55.     exit(1);
  56. }
  57.  
  58. open(GIF, $gif_file) || die "Can't open '$gif_file'\n";
  59.  
  60. my(%info) = ReadFileHeader();
  61.  
  62. SkipColorMap($info{'color_map_size'}) if($info{'global_colormap'} == 1);
  63.  
  64. my($image_start) = FindImageDescriptorAndComments();
  65.  
  66. close(GIF);
  67.  
  68. if(defined($new_comment)) {
  69.     InsertComment($gif_file, $new_comment, $image_start, @g_comments);
  70. } else {
  71.     ShowComments(@g_comments);
  72. }
  73.  
  74. ##########################################################################
  75. #
  76. # The Header of the file contains two 3 byte fields.
  77. # Bytes 0-2 - 'GIF' (GIF file identifier)
  78. # Bytes 3-5 - version number (either '87a' or '89a')
  79. #
  80. # The Logical Screen Descriptor is located directly after the Header.
  81. # It is a seven byte block orginized as:
  82. #
  83. # Bytes 0 & 1 - width (16 bit little-endian)
  84. # Bytes 2 & 3 - height (16 bit little-endian)
  85. # Byte 4 - four fields packed together
  86. #    1 bit  - Global Color Table Flag (bit 7)
  87. #    3 bits - Color Resolution
  88. #    1 bit  - Sort Flag
  89. #    3 bits - Size of the Global Color Table (bits 0-2)
  90. # Byte 5 - Background Color Index
  91. # Byte 6 - Pixel Aspect Ratio
  92. #
  93. # Returns a hash table with fields for each piece of information found.
  94. #
  95. ##########################################################################
  96. sub ReadFileHeader {
  97.     my($tmp, $tmp_packed) = '';
  98.     my(%info) = ();
  99.  
  100.     read(GIF, $tmp, 3);
  101.     if($tmp ne 'GIF') {
  102.         die "This is not a GIF file.\n";
  103.     }
  104.  
  105.     read(GIF, $tmp, 3);
  106.     if($tmp ne '87a' && $tmp ne '89a') {
  107.         die "The version '$tmp' is invalid for a GIF file.\n";
  108.     }
  109.  
  110.     $info{'version'} = $tmp;
  111.  
  112.     # height and width are 2 byte (16 bit little-endian) numbers
  113.     read(GIF, $tmp, 2);
  114.     $info{'width'} = unpack("v", $tmp);
  115.  
  116.     read(GIF, $tmp, 2);
  117.     $info{'height'} = unpack("v", $tmp);
  118.  
  119.     read(GIF, $tmp, 1);
  120.     $tmp_packed = unpack("C", $tmp);
  121.  
  122.     $info{'color_map_size'} = 2 << ($tmp_packed & 0x07);
  123.     $info{'sort_flag'} = ($tmp_packed & 0x08) >> 3;
  124.     $info{'color_res'} = (($tmp_packed & 0x70) >> 3) + 1;
  125.     $info{'global_colormap'} = ($tmp_packed & 0x80) >> 7;
  126.  
  127.     read(GIF, $tmp, 1);
  128.     $info{'background'} = unpack("C", $tmp);
  129.  
  130.     read(GIF, $tmp, 1);
  131.     $info{'aspect_ratio'} = unpack("C", $tmp);
  132.  
  133.     return(%info);
  134. }
  135.  
  136. ##########################################################################
  137. #
  138. # Ignore the portion of the GIF file where the colormap is listed
  139. #
  140. # $colormap_size - number of colors described with RGB triplets (3 bytes)
  141. #
  142. ##########################################################################
  143. sub SkipColorMap {
  144.     my($colormap_size) = @_;
  145.  
  146.     # RGB triplets are used to describe each color
  147.     read(GIF, $tmp, 3 * $colormap_size);
  148. }
  149.  
  150. ##########################################################################
  151. #
  152. # Process the remainder of the file before the image data starts.  This
  153. # is where any comments should be located in the file.  According to the
  154. # GIF spec, comment blocks could occur after the image data, but for the
  155. # purpose of this program they can be ignored.
  156. #
  157. # Returns the location in the file where the image data begins.
  158. #
  159. ##########################################################################
  160. sub FindImageDescriptorAndComments {
  161.     my($image_start) = 0;
  162.     # or set to tell(GIF) right now
  163.  
  164.     while(read(GIF, $c, 1)) {
  165.         $c_ord = ord($c);
  166.         if($c_ord == 0x3B) {
  167.             # GIF terminator (end of file)
  168.             last;
  169.         } elsif($c_ord == 0x21) {
  170.             ProcessExtension();
  171.             next;
  172.         } elsif($c_ord == 0x2C) {
  173.             # Start of the image descriptor
  174.             $image_start = tell(GIF) - 1;
  175.             last;
  176.         } else {
  177.             print "Unknown char [$c][$c_ord]\n";
  178.             next;
  179.         }
  180.     }
  181.     return($image_start)
  182. }
  183.  
  184. ##########################################################################
  185. #
  186. # Process GIF comment extension blocks and ignore all others.  Comments
  187. # that are found will be added to the @g_comments array as a string with
  188. # three colon separated fields fields.
  189. #
  190. #    comment_start_offset:comment_length:comment_text
  191. #
  192. ##########################################################################
  193. sub ProcessExtension {
  194.     my($label, $comment, $comment_segment) = '';
  195.     my($start, $length) = 0;
  196.  
  197.     read(GIF, $label, 1);
  198.  
  199.     if(ord($label) == 0xFE) {
  200.         # Comment extension
  201.         $start = tell(GIF) - 2;
  202.         
  203.         while(GetDataBlock(\$comment_segment)) {
  204.             $comment .= $comment_segment;
  205.         }
  206.  
  207.         $length = tell(GIF) - $start;
  208.  
  209.         push(@g_comments, "$start:$length:$comment");
  210.     } else {
  211.         # Ignore other extension types
  212.  
  213.         # Graphic Control Extension - 0xF9
  214.         # Plain Text Label - 0x01
  215.         # Application Extension - 0xFF
  216.  
  217.         while(GetDataBlock()) {};
  218.     }
  219. }
  220.  
  221. ##########################################################################
  222. #
  223. # Data blocks begin with a single byte which is the length of the
  224. # data contained in the block.  Any data read will be stored in the
  225. # $buffer_ref parameter reference passed to this routine.  If the
  226. # incorrect amount of data is read, the program will exit.
  227. #
  228. # Returns the number of bytes of data read.  0 will be returned for
  229. # an empty data block.
  230. #
  231. ##########################################################################
  232. sub GetDataBlock {
  233.     my($buffer_ref) = @_;
  234.  
  235.     my($size) = 0;    
  236.  
  237.     read(GIF, $size, 1);
  238.     $size = ord($size);
  239.  
  240.     return(0) if($size == 0);
  241.  
  242.     if(read(GIF, $$buffer_ref, $size) != $size) {
  243.         die "Error reading data block.\n";
  244.     } else {
  245.         return($size);
  246.     }
  247. }
  248.  
  249. ##########################################################################
  250. #
  251. # Insert a new comment into the GIF file.  Any existing comments will
  252. # be overwritten.  The main steps used are:
  253. #    1. Read the entire GIF file into a string
  254. #    2. Strip out existing comments
  255. #    3. Print out the GIF up to the start of the image data
  256. #    4. Print out the new comment
  257. #    5. Print the remainder of the image data
  258. #
  259. ##########################################################################
  260. sub InsertComment {
  261.     my($file, $comment, $image_start, @existing_comments) = @_;
  262.  
  263.     open(GIF_IN, "$file") || die "Can't open '$file'\n";
  264.     my($f) = join('', <GIF_IN>);
  265.     close(GIF_IN);
  266.  
  267.     # Remove existing comments
  268.     foreach my $info (reverse(@existing_comments)) {
  269.         my($start, $length, $old_comment) = split(/:/, $info, 3);
  270.         substr($f, $start, $length) = "";
  271.         $image_start -= $length;        
  272.     }
  273.  
  274.     open(GIF_OUT, ">$file") || die "Can't write to '$file'\n";
  275.  
  276.     # Print the file to the point where the comment is inserted
  277.     print GIF_OUT substr($f, 0, $image_start);
  278.  
  279.     if($comment ne '') {
  280.         # Add the comment
  281.         # First print comment extension block identifier
  282.         print GIF_OUT pack("c", 0x21);
  283.         print GIF_OUT pack("c", 0xFE);
  284.  
  285.         # Break up comment into maximum 254 byte data block chunks
  286.         while(length($comment) > 254) {
  287.             print GIF_OUT pack("c", 254);
  288.             print GIF_OUT substr($comment, 0, 254);
  289.             $comment = substr($comment, 254);
  290.         }
  291.  
  292.         # Print remainder of the comment
  293.         print GIF_OUT pack("c", length($comment));
  294.         print GIF_OUT $comment;
  295.  
  296.         # Terminating empty data block for the comment
  297.         print GIF_OUT pack("c", 0x00);    
  298.     }
  299.  
  300.     # Print the remainder of the image
  301.     print GIF_OUT substr($f, $image_start);
  302.  
  303.     close(GIF_OUT);
  304. }
  305.  
  306. ##########################################################################
  307. #
  308. # Display any comments found
  309. #
  310. ##########################################################################
  311. sub ShowComments {
  312.     my(@comments) = @_;
  313.  
  314.     foreach my $info (@comments) {
  315.         my($start, $length, $comment) = split(/:/, $info, 3);
  316.         print "$comment\n";
  317.     }
  318. }
  319.  
  320. ##########################################################################
  321. #
  322. # Display usage information
  323. #
  324. ##########################################################################
  325. sub ShowUsage {
  326.  
  327.     my($name) = $0;
  328.     $name =~ s#^.*/([^/]+)$#$1#;
  329.  
  330. print <<USAGE;
  331. Usage: $name [-h] [-c comment] file
  332.  
  333.     -h = Print this usage statement
  334.  
  335.     -c = Specify a comment string to add to the GIF file
  336.  
  337. With no options, any existing comments will be shown.  When a new comment 
  338. is specified, all existing comments will be overwritten.
  339.  
  340. USAGE
  341.  
  342. }
  343.